package com.guguskins.back.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guguskins.back.common.constant.InventoryConstant;
import com.guguskins.back.common.constant.RabbitMQConstant;
import com.guguskins.back.common.exception.GlobalMsgException;
import com.guguskins.back.common.mq.RabbitMQProducers;
import com.guguskins.back.entity.*;
import com.guguskins.back.entity.bo.RollEditBo;
import com.guguskins.back.entity.vo.PageVo;
import com.guguskins.back.entity.vo.UserVo;
import com.guguskins.back.mapper.GuRollMapper;
import com.guguskins.back.service.*;
import com.guguskins.back.utils.CommonUtil;
import com.guguskins.back.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.thymeleaf.util.DateUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author mybatis-plus
 * @since 2021-06-04
 */
@Service
@Slf4j
public class GuRollServiceImpl extends ServiceImpl<GuRollMapper, GuRoll> implements GuRollService {

    @Autowired
    private GuRollMapper rollMapper;

    @Autowired
    private GuUserService userService;

    @Autowired
    private GuGoodsService goodsService;

    @Autowired
    private GuRollUserService rollUserService;

    @Autowired
    private GuRollGoodsService rollGoodsService;

    @Autowired
    private GuInventoryService inventoryService;

    @Autowired
    private GuRollRecordService rollRecordService;

    @Autowired
    private GuCurrencyRecordService currencyRecordService;

    @Override
    public PageVo<GuRoll> getList(int p, int size, Long userId, int state) {
        long count = rollMapper.getCount(userId, state);

        List<GuRoll> rollList = new ArrayList<>();
        if (count > 0) {
            int index = (p - 1) * size;
            rollList = rollMapper.getPageData(index, size, userId, state);
            // 格式化数据
            rollList.stream()
                    .filter(roll -> !CollectionUtils.isEmpty(roll.getGoodsList()))
                    .forEach(roll -> roll.setSimpleGoodsList(goodsService.format(roll.getGoodsList())
                            .stream()
                            .sorted(Comparator.comparing(GuGoods::getGoodsPrice).reversed())
                            .limit(3).collect(Collectors.toList())));
            rollList.forEach(this::initRoll);
            // 排序
            rollList = rollList.stream().sorted(Comparator.comparing(GuRoll::getRollSort).thenComparing(GuRoll::getTotalPrice).reversed()).collect(Collectors.toList());
        }
        return new PageVo<>(rollList, count);
    }

    @Override
    public GuRoll getDetail(Long userId, long rollId) {
        GuRoll roll = rollMapper.getDetail(rollId);
        // 获取奖品池
        List<GuGoods> goodsList = goodsService.getGoodsListByRollId(rollId, roll.getRollState());
        goodsList = goodsList.stream().sorted(Comparator.comparing(GuGoods::getGoodsPrice).reversed()).collect(Collectors.toList());
        roll.setSimpleGoodsList(CollectionUtils.isEmpty(goodsList) ? new ArrayList<>() : goodsService.format(goodsList).stream().sorted(Comparator.comparing(GuGoods::getGoodsPrice).reversed()).collect(Collectors.toList()));
        roll.setGoodsList(goodsList);
        this.initRoll(roll);

        // 判断是不是已参与
        if (!ObjectUtils.isEmpty(userId)) {
            int count = rollUserService.count(new LambdaQueryWrapper<GuRollUser>() {
                {
                    eq(GuRollUser::getRuUserId, userId);
                    eq(GuRollUser::getRuRollId, rollId);
                }
            });
            if (count > 0) {
                roll.setRollState(3);
            }
        }
        return roll;
    }

    @Override
    public PageVo<GuUser> participation(int p, int size, long rollId) {
        return userService.getPageByRollId(p, size, rollId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean participate(Long userId, GuRoll roll) {
        GuRoll guRoll = this.getById(roll.getRollId());
        if (!guRoll.getRollState().equals(1)) {
            throw new GlobalMsgException("该房已开奖");
        }
        // 如果是创建者禁止参加
        if (guRoll.getRollUserId().equals(userId)) {
            throw new GlobalMsgException("你是该roll房房主");
        }

        // 验证密码
        if (!ObjectUtils.isEmpty(guRoll.getRollPassword())) {
            if (ObjectUtils.isEmpty(roll.getRollPassword()) || !guRoll.getRollPassword().equals(roll.getRollPassword())) {
                throw new GlobalMsgException("密码错误");
            }
        }

        // 查看是否到达上限
        if (!roll.getRollNumberLimit().equals(0) && rollUserService.count(new LambdaQueryWrapper<GuRollUser>() {
            {
                eq(GuRollUser::getRuUserId, userId);
                eq(GuRollUser::getRuRollId, roll.getRollId());
            }
        }) >= roll.getRollNumberLimit()) {
            throw new GlobalMsgException("人数已达上限");
        }

        //校验充值条件是否符合
        if (!ObjectUtils.isEmpty(roll.getRollDateLimit()) && !ObjectUtils.isEmpty(roll.getRollAmountLimit())) {
            BigDecimal totalAmount = currencyRecordService.getObj(new QueryWrapper<GuCurrencyRecord>() {
                {
                    select("COALESCE(SUM(cr_amount),0.00) as total");
                    eq("cr_type", 0);
                    eq("cr_source_type", 1);
                    ge("cr_create_time", DateUtil.getTime(roll.getRollDateLimit()));
                }
            }, o -> new BigDecimal(o.toString()));
            if (totalAmount.compareTo(roll.getRollAmountLimit()) < 0) {
                throw new GlobalMsgException("该roll房有充值限制，充值激活后才可以参与");
            }
        }

        // 查看是否参与
        int count = rollUserService.count(new LambdaQueryWrapper<GuRollUser>() {
            {
                eq(GuRollUser::getRuUserId, userId);
                eq(GuRollUser::getRuRollId, roll.getRollId());
            }
        });
        if (count > 0) {
            throw new GlobalMsgException("您已参过与该roll房");
        }
        GuUser user = userService.getById(userId);
        GuRollUser rollUser = new GuRollUser();
        rollUser.setRuRollId(roll.getRollId());
        rollUser.setRuUserId(user.getUserId());
        rollUser.setRuSteamId(user.getUserSteamId());
        return rollUser.insert();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean create(Long userId, GuRoll roll) {
        // 校验参数
        CommonUtil.checkParams(roll.getRollTitle(), roll.getInventoryIds());

        if (roll.getRollLotteryTime().compareTo(DateUtil.getTimeAddHours(1)) <= 0) {
            throw new GlobalMsgException("开奖时间不能低于一个小时");
        }
        GuUser user = userService.getById(userId);
        if (ObjectUtils.isEmpty(user)) {
            throw new GlobalMsgException("未知用户");
        }
        int titleCount = this.count(new LambdaQueryWrapper<GuRoll>() {
            {
                eq(GuRoll::getRollTitle, roll.getRollTitle());
            }
        });
        if (titleCount > 0) {
            throw new GlobalMsgException("roll房标题已存在");
        }
        int userCount = this.count(new LambdaQueryWrapper<GuRoll>() {
            {
                eq(GuRoll::getRollUserId, roll.getRollUserId());
                eq(GuRoll::getRollState, 1);
            }
        });
        if (userCount > 0) {
            throw new GlobalMsgException("用户已创建过roll房");
        }

        roll.setRollUserId(user.getUserId());
        roll.setRollSteamId(user.getUserSteamId());
        // 插入roll房
        if (roll.insert()) {
            List<Long> inventoryIds = roll.getInventoryIds();
            if (inventoryIds.size() <= 0) {
                throw new GlobalMsgException("请选择奖品池");
            }
            List<GuInventory> inventories = inventoryService.listByIds(inventoryIds);
            if (inventoryIds.size() != inventories.size() || !inventories.stream().allMatch(inventory -> inventory.getInventoryState().equals(InventoryConstant.State.INVENTORY))) {
                throw new GlobalMsgException("参数错误");
            }
            inventories.forEach(inventory -> {
                // 创建roll放商品池商品
                GuRollGoods rollGoods = new GuRollGoods();
                rollGoods.setRgRollId(roll.getRollId());
                rollGoods.setRgGoodsId(inventory.getInventoryItemId());
                rollGoods.setRgInventoryId(inventory.getInventoryId());
                // 插入商品池商品
                if (rollGoods.insert()) {
                    // 锁库存
                    inventory.setInventoryState(InventoryConstant.State.ROLL_GOODS_LIST);
                    if (inventory.updateById()) {
                        // 发送定时任务进行开奖
                        RabbitMQProducers.convertAndSend(RabbitMQConstant.Exchange.ROLL_DELAY, RabbitMQConstant.RoutingKey.ROLL_DELAY, roll.getRollLotteryTime(), roll.getRollId());
                    } else {
                        throw new GlobalMsgException("创建失败");
                    }
                } else {
                    throw new GlobalMsgException("创建失败");
                }
            });
        } else {
            throw new GlobalMsgException("创建失败");
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean lottery(Long rollId) {

        GuRoll roll = this.getById(rollId);
        //查询roll房状态
        if (ObjectUtils.isEmpty(roll) || !roll.getRollState().equals(1)) {
            log.info("{}已被删除或已开奖", rollId);
            return true;
        }

        List<GuRollGoods> rollGoodsList = rollGoodsService.list(new LambdaQueryWrapper<GuRollGoods>() {
            {
                eq(GuRollGoods::getRgRollId, rollId);
            }
        });

        List<GuRollUser> rollUserList = rollUserService.list(new LambdaQueryWrapper<GuRollUser>() {
            {
                eq(GuRollUser::getRuRollId, rollId);
                eq(GuRollUser::getRuState, 1);
            }
        });

        // 查询roll房开奖记录表
        List<GuRollRecord> rollRecordList = rollRecordService.list(new LambdaQueryWrapper<GuRollRecord>() {
            {
                eq(GuRollRecord::getRrRollId, rollId);
            }
        });
        List<GuRollRecord> lotteryResult = new ArrayList<>();
        // 如果有指定
        if (!CollectionUtils.isEmpty(rollRecordList)) {
            // 剔除指定奖品和用户
            rollGoodsList = rollGoodsList.stream().filter(rollGoods -> rollRecordList.stream().anyMatch(rollRecord -> rollGoods.getRgGoodsId().equals(rollRecord.getRrGoodsId()))).collect(Collectors.toList());
            rollUserList = rollUserList.stream().filter(rollUser -> rollRecordList.stream().anyMatch(rollRecord -> rollUser.getRuUserId().equals(rollRecord.getRrUserId()))).collect(Collectors.toList());
        }

        // 接收回填库存
        List<GuRollGoods> backFillGoodsList = new ArrayList<>();
        // 比较饰品和参与人数
        if (rollGoodsList.size() > rollUserList.size()) {
            // 饰品多余参与人数
            Collections.shuffle(rollUserList);
            List<GuRollGoods> finalRollGoodsList = rollGoodsList;
            rollUserList.forEach(rollUser -> {
                int random = (int) (Math.random() * finalRollGoodsList.size());
                GuRollGoods rollGoods = finalRollGoodsList.get(random);
                GuRollRecord rollRecord = new GuRollRecord();
                rollRecord.setRrRollId(rollId);
                rollRecord.setRrUserId(rollUser.getRuUserId());
                rollRecord.setRrSteamId(rollUser.getRuSteamId());
                rollRecord.setRrGoodsId(rollGoods.getRgGoodsId());
                lotteryResult.add(rollRecord);
                finalRollGoodsList.remove(random);
            });
            backFillGoodsList = finalRollGoodsList;
        } else {
            // 参与人数多余饰品
            // 人数多正常开奖
            Collections.shuffle(rollGoodsList);
            List<GuRollUser> finalRollUserList = rollUserList;
            rollGoodsList.forEach(rollGoods -> {
                int random = (int) (Math.random() * finalRollUserList.size());
                GuRollUser rollUser = finalRollUserList.get(random);
                GuRollRecord rollRecord = new GuRollRecord();
                rollRecord.setRrRollId(rollId);
                rollRecord.setRrUserId(rollUser.getRuUserId());
                rollRecord.setRrSteamId(rollUser.getRuSteamId());
                rollRecord.setRrGoodsId(rollGoods.getRgGoodsId());
                lotteryResult.add(rollRecord);
                finalRollUserList.remove(random);
            });
        }
        // 记录抽奖结果
        if (rollRecordService.saveBatch(lotteryResult)) {
            // 结果显示
            // 发放奖品
            List<GuRollRecord> rollGoodsListPrize = lotteryResult.stream().filter(result -> !ObjectUtils.isEmpty(result.getRrUserId())).collect(Collectors.toList());

            rollGoodsListPrize.forEach(prize -> inventoryService.addInventory(prize.getRrUserId(), prize.getRrSteamId(), InventoryConstant.Type.GOODS, InventoryConstant.Source.ROLL, rollId, prize.getRrGoodsId(), null));

            // 回填库存
            if (!ObjectUtils.isEmpty(backFillGoodsList) && backFillGoodsList.size() > 0) {
                List<GuRollGoods> finalBackFillGoodsList = backFillGoodsList;
                inventoryService.update(new LambdaUpdateWrapper<GuInventory>() {
                    {
                        in(GuInventory::getInventoryId, finalBackFillGoodsList.stream().map(GuRollGoods::getRgInventoryId).collect(Collectors.toList()));
                        set(GuInventory::getInventoryState, InventoryConstant.State.INVENTORY);
                    }
                });
            }
        }

        // 修改roll房状态
        roll.setRollState(2);
        if (!roll.updateById()) {
            throw new GlobalMsgException("修改roll房状态失败");
        }

        return true;
    }

    private void initRoll(GuRoll roll) {
        if (CollectionUtils.isEmpty(roll.getGoodsList())) {
            roll.setGoodsList(new ArrayList<>());
            roll.setGoodsNum(0L);
            roll.setTotalPrice(BigDecimal.ZERO);
        } else {
            // 计算饰品数
            roll.setGoodsNum((long) roll.getGoodsList().size());
            // 计算总价格
            roll.setTotalPrice(roll.getGoodsList().stream().map(GuGoods::getGoodsPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        }

        if (CollectionUtils.isEmpty(roll.getUsers())) {
            roll.setUsers(new ArrayList<>());
            roll.setUserNum(0L);
        } else {
            // 计算人数
            roll.setUserNum((long) roll.getUsers().size());
        }

        // 设置密码为*
        if (!ObjectUtils.isEmpty(roll.getRollPassword())) {
            roll.setRollPassword("******");
        }
    }

    @Override
    public boolean backSave(GuRoll roll) {

        return this.create(roll.getRollUserId(), roll);
    }

    @Override
    public boolean backEdit(RollEditBo rollEditBo) {
        CommonUtil.checkParams(rollEditBo.getRollTitle());
        if (rollEditBo.getRollLotteryTime().compareTo(new Date()) <= 0) {
            throw new GlobalMsgException("时间不能小于当前时间");
        }
        GuRoll roll = new GuRoll();
        BeanUtils.copyProperties(rollEditBo, roll);
        int count = this.count(new LambdaQueryWrapper<GuRoll>() {
            {
                eq(GuRoll::getRollTitle, roll.getRollTitle());
            }
        });
        if (count > 0) {
            throw new GlobalMsgException("roll房标题已存在");
        }
        return roll.updateById();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean backDeleteByRollId(long rollId) {
        List<GuRollGoods> rollGoodsList = rollGoodsService.list(new LambdaQueryWrapper<GuRollGoods>() {
            {
                eq(GuRollGoods::getRgRollId, rollId);
            }
        });
        if (!CollectionUtils.isEmpty(rollGoodsList)) {
            final List<Long> list = rollGoodsList.stream().map(GuRollGoods::getRgInventoryId).collect(Collectors.toList());
            if (list.size() > 0) {
                inventoryService.update(new LambdaUpdateWrapper<GuInventory>() {
                    {
                        set(GuInventory::getInventoryState, InventoryConstant.State.INVENTORY);
                        in(GuInventory::getInventoryId, list);
                    }
                });
            }
            rollGoodsService.remove(new LambdaQueryWrapper<GuRollGoods>() {
                {
                    eq(GuRollGoods::getRgRollId, rollId);
                }
            });
            rollUserService.remove(new LambdaQueryWrapper<GuRollUser>() {
                {
                    eq(GuRollUser::getRuRollId, rollId);
                }
            });
            rollRecordService.remove(new LambdaQueryWrapper<GuRollRecord>() {
                {
                    eq(GuRollRecord::getRrRollId, rollId);
                }
            });
            this.remove(new LambdaQueryWrapper<GuRoll>() {
                {
                    eq(GuRoll::getRollId, rollId);
                }
            });
        }
        return true;
    }

    @Override
    public PageVo<GuRoll> backList(int p, int size, String searchKey) {
        IPage<GuRoll> page = new Page<>(p, size);
        page = this.page(page, new LambdaQueryWrapper<GuRoll>() {
            {
                like(!ObjectUtils.isEmpty(searchKey), GuRoll::getRollSteamId, searchKey);
                or();
                like(!ObjectUtils.isEmpty(searchKey), GuRoll::getRollTitle, searchKey);
            }
        });
        return new PageVo<>(page.getRecords(), page.getTotal());
    }

    @Override
    public List<UserVo> backUsersByRollId(long rollId, String searchKey) {
        return userService.backUsersByRollId(rollId, searchKey);
    }

    @Override
    public List<GuGoods> backGoodsByRollId(long rollId, String searchKey) {
        List<GuGoods> goodsListByRollId = goodsService.getGoodsListByRollId(rollId, searchKey);
        return goodsListByRollId.stream().filter(goods -> ObjectUtils.isEmpty(goods.getUserSteamId())).collect(Collectors.toList());
    }

    @Override
    public boolean backPrizeAssign(GuRollRecord rollRecord) {

        GuRollRecord record = rollRecordService.getOne(new LambdaQueryWrapper<GuRollRecord>() {
            {
                eq(GuRollRecord::getRrUserId, rollRecord.getRrUserId());
                eq(GuRollRecord::getRrRollId, rollRecord.getRrRollId());
                last("limit 1");
            }
        });

        GuRollGoods rollGoods = rollGoodsService.getOne(new LambdaQueryWrapper<GuRollGoods>() {
            {
                eq(GuRollGoods::getRgInventoryId, rollRecord.getInventoryId());
                eq(GuRollGoods::getRgRollId, rollRecord.getRrRollId());
                last("limit 1");
            }
        });
        if (ObjectUtils.isEmpty(rollGoods)) {
            throw new GlobalMsgException("未知饰品");
        }

        GuInventory newInventory = inventoryService.getById(rollRecord.getInventoryId());
        newInventory.setInventoryState(InventoryConstant.State.INVENTORY);
        newInventory.updateById();

        if (ObjectUtils.isEmpty(record)) {
            GuUser user = userService.getById(rollRecord.getRrUserId());
            rollRecord.setRrGoodsId(newInventory.getInventoryItemId());
            rollRecord.setRrSteamId(user.getUserSteamId());
            rollRecord.setRrRollGoodsId(rollGoods.getRgId());
            rollRecord.setRrType(2);
            return rollRecord.insert();
        } else {
            //释放原有奖品
            GuRollGoods oldRollGoods = rollGoodsService.getById(record.getRrRollGoodsId());
            GuInventory oldInventory = inventoryService.getById(oldRollGoods.getRgInventoryId());
            oldInventory.setInventoryState(InventoryConstant.State.INVENTORY);
            oldInventory.updateById();

            //绑定新的奖品
            rollRecord.setRrRollGoodsId(rollGoods.getRgGoodsId());
            rollRecord.setRrGoodsId(rollGoods.getRgId());
            return record.updateById();
        }

    }

    @Override
    public boolean backCancelPrizeAssign(GuRollRecord rollRecord) {
        return rollRecordService.removeById(rollRecord.getRrId());
    }
}
